/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.cloudbus.cloudsim.power.models.scheduling.client;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.cloudbus.cloudsim.Cloudlet;
import org.cloudbus.cloudsim.Log;
import org.cloudbus.cloudsim.Vm;
import org.cloudbus.cloudsim.core.CloudSimTags;
import org.cloudbus.cloudsim.core.SimEntity;
import org.cloudbus.cloudsim.core.SimEvent;
import org.cloudbus.cloudsim.power.models.scheduling.FrontEnd;
import org.cloudbus.cloudsim.power.models.scheduling.Middleware;

/**
 *
 * @author Márcio Costa Junior - University of São Paulo, Montana State University
 * 
 * Represents a client of the cloud
 */
public class Client extends SimEntity{
    
    //Network delay between the middleware and the requirer
    public static long ZONEDELAY = 5;
    
    //Client's zone
    protected int zone;
    
    //If this client has more priority than the others
    private boolean realTime;
    
    private Request request;
    
    private Middleware mid;
    
    private int period;
    
    private ClientManager cm;
    
    public Client(String name, int zone, ClientManager cm, int period){
        super(name);
        
        setZone(zone);
        setRealTime(false);
        setCm(cm);
        this.period = period;
    }

    @Override
    public void startEntity() {
        Log.printLine("The client number #" + getId() + " from zone " + getZone() + " is starting");
        //Waits for the network delay to send the message to its specific middleware
        schedule(getId(), 0, CloudSimTags.CLIENT_ASK, getRequest());
    }

    @Override
    public void processEvent(SimEvent ev) {
        switch (ev.getTag()){
			
                case CloudSimTags.CLIENT_ASK:
                       	askForMiddleware(ev.getData());
                break;
                case CloudSimTags.CLIENT_ANSWER:
                       	sendRequestRunTime(ev.getData());
                break;
                case CloudSimTags.NEW_CLIENT:
                        createClient(ev.getData());	
                break;
                default:
                       Log.printLine("Message not found");
		}
    }
    
    protected void createClient(Object arg){
        int period = (Integer) arg;
        this.period--;
        
        Request rq = new Request(getRequest().getTimeFrame(), getRequest().getBrokerId());
        rq.setStart(getRequest().getEnd());
        rq.setEnd(getRequest().getEnd() + (getRequest().getEnd() - getRequest().getStart()));
        rq.generateJobsDristribution();
        rq.setClientId(getId());
        
        send(getId(), 0, CloudSimTags.CLIENT_ASK, rq);
        
//        setRequest(rq);
//        schedule(getId(), 0, CloudSimTags.CLIENT_ASK);
       
//        Client client = new Client(getName(), zone, cm, period);
//        client.setRequest(rq);
                           
        if(period - 1 > 0){
            send(getId(), Client.ZONEDELAY + getRequest().getTimeFrame(), CloudSimTags.NEW_CLIENT, period - 1);
        }
        
        
    }
    
    protected void askForMiddleware(Object obj){
        
        Request rq = (Request) obj;
        
        send(getCm().getId(), 0, CloudSimTags.CLIENT_ASK, rq);  
    }
    
     
    //Method responsible to send one request per time period in run-time
    protected void sendRequestRunTime(Object obj){
        
        AnswerPackage pkg = (AnswerPackage) obj;
        
        Log.printLine("Client #" + getId() + " is sending a request");
        
//        FrontEnd frontEnd = getMid().filterRequest(getRequest().getService());
        ArrayList<Vm> vmList = new ArrayList<Vm>();
        ArrayList<Cloudlet> clList = new ArrayList<Cloudlet>();
//     
//        for(Job job : getRequest().getJobs()){
//            vmList.add(job.getVm());
//            clList.add(job.getCloudlet());
//        }
//        
//        try {
//            frontEnd.createVmAndCloudlet(vmList, clList);
//        } catch (InterruptedException ex) {
//            Log.printLine("It's not possible to send all these jobs");
//        }
//        
//        if(period - 1 > 0)
//            send(getId(), Client.ZONEDELAY + getRequest().getTimeFrame(), CloudSimTags.NEW_CLIENT, period - 1);    
    
        Middleware middleware = pkg.getMids().get(0);

        for(int i = 0; i < pkg.getMids().size(); i++){
            vmList.add(pkg.getJobs().get(i).getVm());
            clList.add(pkg.getJobs().get(i).getCloudlet());
            
            if(middleware.getId() != pkg.getMids().get(i).getId()){
                try {
                    middleware.getClassList().get(getRequest().getService()).getFrontEnd().createVmAndCloudlet(vmList, clList);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
                }
                vmList = new ArrayList<Vm>();
                clList = new ArrayList<Cloudlet>();
                
                middleware = pkg.getMids().get(i);
            }
        }
        
        if(vmList.size() > 0){
           
//            middleware.getClassList().get(getRequest().getService()).getFrontEnd().submitVmList(vmList);
//            middleware.getClassList().get(getRequest().getService()).getFrontEnd().submitCloudletList(clList);
            try {
                //middleware.getClassList().get(getRequest().getService()).getFrontEnd().startEntity();
                    middleware.getClassList().get(getRequest().getService()).getFrontEnd().createVmAndCloudlet(vmList, clList);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
                }
        }
        if(period - 1 > 0){
            send(getId(), Client.ZONEDELAY + getRequest().getTimeFrame(), CloudSimTags.NEW_CLIENT, period - 1);    
        }
        
    }

    @Override
    public void shutdownEntity() {
        Log.printLine("The client number #" + getId() + " from zone " + getZone() + " is shutting down");
    }

    /**
     * @return the zone
     */
    public int getZone() {
        return zone;
    }

    /**
     * @param zone the zone to set
     */
    public void setZone(int zone) {
        this.zone = zone;
    }

    /**
     * @return the realTime
     */
    public boolean isRealTime() {
        return realTime;
    }

    /**
     * @param realTime the realTime to set
     */
    public void setRealTime(boolean realTime) {
        this.realTime = realTime;
    }

    /**
     * @return the request
     */
    public Request getRequest() {
        return request;
    }

    /**
     * @param request the request to set
     */
    public void setRequest(Request request) {
        this.request = request;
    }

    /**
     * @return the mid
     */
    public Middleware getMid() {
        return mid;
    }

    /**
     * @param mid the mid to set
     */
    public void setMid(Middleware mid) {
        this.mid = mid;
    }

    /**
     * @return the cm
     */
    public ClientManager getCm() {
        return cm;
    }

    /**
     * @param cm the cm to set
     */
    public void setCm(ClientManager cm) {
        this.cm = cm;
    }
    
}
